<?php

require_once "Pay.Config.php";
require_once "Pay.Exception.php";
require_once "Pay.Log.php";

/**
 *
 * 数据对象基础类，该类中定义数据类最基本的行为
 * @author jiangwentong
 *
 */
class PayDataBase
{
    protected $values = array();

    /**
     * 设置签名，详见签名生成算法
     * @param string $value
     **/
    public function SetSign()
    {
        $sign = $this->MakeSign();
        $this->values["REQ_HEAD"]['sign'] = $sign;
        return $sign;
    }

    /**
     * 获取签名，详见签名生成算法的值
     * @return 值
     **/
    public function GetSign()
    {
        if (array_key_exists("REQ_HEAD", $this->values)) {
            return $this->values["REQ_HEAD"]['sign'];
        } else if (array_key_exists("REP_HEAD", $this->values)) {
            return $this->values["REP_HEAD"]['sign'];
        } else {
            return false;
        }

    }

    /**
     * 判断签名，详见签名生成算法是否存在
     * @return true 或 false
     **/
    public function IsSignSet()
    {
        // 判断是请求签名还是返回签名
        if (array_key_exists("REQ_HEAD", $this->values)) {
            return array_key_exists('sign', $this->values["REQ_HEAD"]);
        } else if (array_key_exists("REP_HEAD", $this->values)) {
            return array_key_exists('sign', $this->values["REP_HEAD"]);
        } else {
            return false;
        }

    }

    /**
     * 输出json字符
     * @throws PayException
     **/
    public function ToJson()
    {
        if (!is_array($this->values) || count($this->values) <= 0) {
            throw new PayException("数组数据异常！");
        }
        return json_encode($this->values);
    }

    /**
     * 将json转为array
     * @param string $json
     * @return array|mixed
     * @throws PayException
     */
    public function FromJson($json)
    {
        if (!$json) {
            throw new PayException("json数据异常！");
        }
        //将JSON转为array
        $this->values = json_decode($json, true);
        return $this->values;
    }

    /**
     * 格式化参数格式化成url参数，用于计算签名
     */
    public function ToUrlParams()
    {
        $buff = "";
        $arr = array();
        // 区别请求和返回，
        if (array_key_exists("REQ_BODY", $this->values)) {
            $arr = $this->values["REQ_BODY"];
        } else if (array_key_exists("REP_BODY", $this->values)) {
            $arr = $this->values["REP_BODY"];
        }
        //格式化参数格式化成url参数
        foreach ($arr as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function MakeSign()
    {
        //签名步骤一：按字典序排序参数

        //区别是请求、还是响应
        if (array_key_exists("REQ_BODY", $this->values)) {
            ksort($this->values["REQ_BODY"], SORT_FLAG_CASE|SORT_STRING );
        } else if (array_key_exists("REP_BODY", $this->values)) {
            ksort($this->values["REP_BODY"], SORT_FLAG_CASE|SORT_STRING );
        }
        $string = $this->ToUrlParams();
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . PayConfig::KEY;
        log::DEBUG("计算签名串：" . $string);
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 获取设置的值
     */
    public function GetValues()
    {
        return $this->values;
    }
}

/**
 *
 * 通用封装输入对象
 * @author jiangwentong
 *
 */
class UnifiedData extends PayDataBase
{
    /**
     * 设置参数
     * @param string $name
     * @param string $value
     **/
    public function SetParam($name, $value)
    {
        $this->values["REQ_BODY"][$name] = $value;
    }

    /**
     * 获取指定参数的值
     * @param string $name
     * @return 值
     **/
    public function GetParam($name)
    {
        return $this->values["REQ_BODY"][$name];
    }

    /**
     * 判断指定参数时候存在
     * @param string $name
     * @return true 或 false
     **/
    public function IsParamSet($name)
    {
        return array_key_exists($name, $this->values["REQ_BODY"]);
    }
}


/**
 *
 * 接口调用结果类
 * @author jiangwentong
 *
 */
class PayResults extends PayDataBase
{
    /**
     *
     * 校验签名
     */
    public function CheckSign()
    {
        if (!$this->IsSignSet()) {
            throw new PayException("签名错误！");
        }

        $sign = $this->MakeSign();

        if ($this->GetSign() == $sign) {
            return true;
        }
        // 调试输出签名错误问题
        LOG::DEBUG("计算签名：" . $sign);
        LOG::DEBUG("响应签名：" . $this->GetSign());
        LOG::DEBUG("响应报文：" . $this->ToJson());
        // throw new PayException("签名不匹配！");
        return false;
    }

    /**
     *
     * 使用数组初始化
     * @param array $array
     */
    public function FromArray($array)
    {
        $this->values = $array;
    }

    /**
     *
     * 使用数组初始化对象
     * @param array $array
     * @param 是否检测签名 $noCheckSign
     * @return PayResults
     */
    public static function InitFromArray($array, $noCheckSign = false)
    {
        $obj = new self();
        $obj->FromArray($array);
        if ($noCheckSign == false) {
            $obj->CheckSign();
        }
        return $obj;
    }

    /**
     *
     * 设置参数
     * @param string $key
     * @param string $value
     */
    public function SetData($key, $value)
    {
        $this->values["REQ_BODY"][$key] = $value;
    }

    /**
     * 将json转为array
     * @param string $json
     * @throws PayException
     * @return array
     */
    public static function Init($json)
    {
        $obj = new self();
        $obj->FromJson($json);

        if ($obj->values['REP_BODY']['rspcode'] == '000000') {
            // $obj->CheckSign();
            LOG::INFO("请求成功");
            LOG::INFO("响应码：" . $obj->values['REP_BODY']['rspcode']);
            LOG::INFO("响应信息：" . urldecode($obj->values['REP_BODY']['rspmsg']));

        } else {
            LOG::WARN("请求失败");
            LOG::WARN("响应码：" . $obj->values['REP_BODY']['rspcode']);
            LOG::WARN("响应信息：" . urldecode($obj->values['REP_BODY']['rspmsg']));
        }

        return $obj->GetValues();
    }
}

/**
 *
 * 回调基础类
 * @author jiangwentong
 *
 */
class PayNotifyReply extends PayDataBase
{
    /**
     *
     * 设置订单状态
     * 订单状态：00-未支付 01-成功 02-失败
     * @param string
     */
    public function SetOrderState($orderState)
    {
        $this->values["REP_BODY"]['orderState'] = $orderState;
    }

    /**
     *
     * 获取订单状态
     * 订单状态：00-未支付 01-成功 02-失败
     * @return string $orderState
     */
    public function GetOrderState()
    {
        return $this->values["REP_BODY"]['orderState'];
    }

    /**
     *
     * 设置订单号，查询订单时使用
     * @param string $orderId
     */
    public function SetOrderId($orderId)
    {
        $this->values["REP_BODY"]['orderId'] = $orderId;
    }

    /**
     * 获取订单号，查询订单时使用
     * @return string
     */
    public function GetOrderId()
    {
        return $this->values["REP_BODY"]['orderId'];
    }

    /**
     *
     * 设置返回参数
     * @param string $key
     * @param string $value
     */
    public function SetData($key, $value)
    {
        $this->values["REP_BODY"][$key] = $value;
    }
}

/**
 * 交易代码定义
 */
class TranCode
{
    /**
     * 商户入驻
     */
    public static $TRAN_CODE_MERCHANT_BIND = "PN0001";

    /**
     * 商户查询
     */
    public static $TRAN_CODE_MERCHANT_QUERY = "PN0002";

    /**
     * 支付宝H5下单
     */
    public static $TRAN_CODE_ALIPAY_TRADE_CREATE = "PN0003";

    /**
     * 微信公众号支付
     */
    public static $TRAN_CODE_WX_TRADE_CREATE = "PN0004";

    /**
     * 订单查询
     */
    public static $TRAN_CODE_ORDER_QUERY = "PN0005";

    /**
     * 订单关闭
     */
    public static $TRAN_CODE_ORDER_CLOSE = "PN0006";

    /**
     * 交易退款
     */
    public static $TRAN_CODE_ORDER_REFUND = "PN0007";

    /**
     * 退款查询
     */
    public static $TRAN_CODE_ORDER_REFUND_QUERY = "PN0008";

    /**
     * 支付宝刷卡支付
     */
    public static $TRAN_CODE_ALIPAY_TRADE_PAY = "PN0011";

    /**
     * 微信刷卡支付
     */
    public static $TRAN_CODE_WX_TRADE_PAY = "PN0012";

    /**
     * 订单撤销
     */
    public static $TRAN_CODE_ORDER_REVERSE = "PN0013";

    /**
     * 微信扫码支付
     */
    public static $TRAN_CODE_WX_TRADE_PRE_CREATE = "PN0014";

    /**
     * 支付宝扫码支付
     */
    public static $TRAN_CODE_ALIPAY_TRADE_PRE_CREATE = "PN0015";

    /**
     * 对公账户验证鉴权
     */
    public static $TRAN_CODE_BANK_VERIFY_MONEY = "PN0016";
}
